package msg

import (
	"encoding/binary"
	"fmt"
	"github.com/golang/protobuf/proto"
	"reflect"
)

var (
	Marshal_Error_LengthShort = fmt.Errorf("data length too short")
	Marshal_Error_MessageType = fmt.Errorf("message type is wrong")
	Marshal_Error_MessageId	= fmt.Errorf("message id not find")
)

type IParser interface {
	Unmarshal(data []byte) (*Message, uint16, error)
	Marshal(msg *Message) ([][]byte, error)
}

func Unmarshal(data []byte) (*MessageHead, []byte, error) {
	if len(data) < MessageHead_Flow_Len {
		return nil, nil, Marshal_Error_LengthShort
	}

	index := 0
	head := &MessageHead{}
	head.Type = MessageType(data[index])
	index += 1
	if head.Type != MessageType_Msg && head.Type != MessageType_Rpc {
		return nil, nil, Marshal_Error_MessageType
	}

	head.Index = binary.LittleEndian.Uint32(data[index : index+4])
	index += 4
	head.Code = binary.LittleEndian.Uint16(data[index : index+2])
	index += 2
	head.MsgId = binary.LittleEndian.Uint16(data[index : index+2])
	index += 2

	if head.Type == MessageType_Rpc {
		if len(data) < MessageHead_Flow_Len + 2 {
			return nil, nil, Marshal_Error_LengthShort
		}
		head.RpcLen = binary.LittleEndian.Uint16(data[index : index+2])
		index += 2
		if head.RpcLen > 0 {
			if len(data) < MessageHead_Flow_Len + 2 + int(head.RpcLen) {
				return nil, nil, Marshal_Error_LengthShort
			}
			head.RpcMethod = string(data[index : index+int(head.RpcLen)])
			index += int(head.RpcLen)
		}
	}

	return head, data[index:], nil
}


func Marshal(msg *Message) ([]byte, error) {
	if msg.Type != MessageType_Msg && msg.Type != MessageType_Rpc {
		return nil, Marshal_Error_MessageType
	}

	var err error

	msg.MsgId = 0
	if msg.Code == 0 && msg.Msg != nil {
		msg.MsgId = GetMessageId(reflect.TypeOf(msg.Msg))
		if msg.MsgId == 0 {
			return nil, Marshal_Error_MessageId
		}
		msg.Bytes,err = proto.Marshal(msg.Msg)
		if err != nil {
			return nil, err
		}
	}
	var rpcLen int
	if msg.Type == MessageType_Rpc {
		rpcLen = 2 + len(msg.RpcMethod)
	}

	data := make([]byte, MessageHead_Flow_Len + len(msg.Bytes) + rpcLen)
	index := 0
	data[0] = byte(msg.Type)
	index += 1
	binary.LittleEndian.PutUint32(data[index:index+4], msg.Index)
	index += 4
	binary.LittleEndian.PutUint16(data[index:index+2], msg.Code)
	index += 2
	binary.LittleEndian.PutUint16(data[index:index+2], msg.MsgId)
	index += 2

	if msg.Type == MessageType_Rpc {
		rpcLen = len(msg.RpcMethod)
		binary.LittleEndian.PutUint16(data[index:index+2], uint16(rpcLen))
		index += 2
		if rpcLen > 0 {
			copy(data[index:index+rpcLen], []byte(msg.RpcMethod))
			index += int(rpcLen)
		}
	}

	if msg.MsgId != 0 {
		copy(data[index:], msg.Bytes)
	}
	return data, nil
}
